#include "my_http_args.h"
#include "comm/my_log.h"

void
my_http_args_add(my_http_args_t *args, my_http_arg_t *arg)
{
    my_http_same_args_t *same_args = NULL;

    my_http_same_args_t target = {.name = arg->name};
    my_rbtree_node_t *same_args_node =
        my_rbtree_lookup(&args->same_args_tree, &target.tree_node);
    if (same_args_node) {
        same_args = (my_http_same_args_t *)same_args_node;
    }

    if (!same_args) {
        same_args = my_pool_mem_calloc(args->pool, sizeof(my_http_same_args_t));
        my_http_same_args_init(same_args);
        same_args->name = arg->name;
        my_rbtree_insert(&args->same_args_tree, &same_args->tree_node);
    }

    my_queue_insert_tail(&same_args->args, &arg->queue_in_same_args);
    my_queue_insert_tail(&args->args, &arg->queue_in_args);
}

void
my_http_args_set(my_http_args_t *args, my_http_arg_t *arg)
{
    my_queue_t              *q = NULL;
    my_http_arg_t           *tmp_arg = NULL;
    my_http_same_args_t     *same_args = NULL;

    my_http_same_args_t target = {.name = arg->name};
    my_rbtree_node_t *same_args_node =
        my_rbtree_lookup(&args->same_args_tree, &target.tree_node);
    if (same_args_node) {
        same_args = (my_http_same_args_t *)same_args_node;
        while (!my_queue_empty(&same_args->args)) {
            q = my_queue_head(&same_args->args);
            tmp_arg = my_queue_data(q, my_http_arg_t, queue_in_same_args);
            my_queue_remove(&tmp_arg->queue_in_same_args);
            my_queue_remove(&tmp_arg->queue_in_args);
        }
    }

    if (!same_args) {
        same_args = my_pool_mem_calloc(args->pool, sizeof(my_http_same_args_t));
        my_http_same_args_init(same_args);
        same_args->name = arg->name;
        my_rbtree_insert(&args->same_args_tree, &same_args->tree_node);
    }

    my_queue_insert_tail(&same_args->args, &arg->queue_in_same_args);
    my_queue_insert_tail(&args->args, &arg->queue_in_args);
}

my_http_arg_t *
my_http_args_get(my_http_args_t *args, my_str_t *name)
{
    my_http_same_args_t target = {.name = *name};
    my_rbtree_node_t *same_args_node =
        my_rbtree_lookup(&args->same_args_tree, &target.tree_node);
    if (!same_args_node) {
        return NULL;
    }

    my_http_same_args_t  *same_args = (my_http_same_args_t *)same_args_node;
    if (my_queue_empty(&same_args->args)) {
        return NULL;
    }

    my_queue_t *q = my_queue_head(&same_args->args);
    return my_queue_data(q, my_http_arg_t, queue_in_same_args);
}

my_http_same_args_t *
my_http_args_values(my_http_args_t *args, my_str_t *name)
{
    my_http_same_args_t target = {.name = *name};
    my_rbtree_node_t *same_args_node =
        my_rbtree_lookup(&args->same_args_tree, &target.tree_node);
    if (!same_args_node) {
        return NULL;
    }

    return (my_http_same_args_t *)same_args_node;
}

void
my_http_args_del(my_http_args_t *args, my_http_arg_t *arg)
{
    (void)args;
    my_queue_remove(&arg->queue_in_same_args);
    my_queue_remove(&arg->queue_in_args);
}

void
my_http_args_del_by_name(my_http_args_t *args, my_str_t *name)
{
    my_http_same_args_t *same_args = NULL;

    my_http_same_args_t target = {.name = *name};
    my_rbtree_node_t *same_args_node =
        my_rbtree_lookup(&args->same_args_tree, &target.tree_node);
    if (!same_args_node) {
        return;
    }

    my_queue_t          *q = NULL;
    my_http_arg_t       *tmp_arg = NULL;
    same_args = (my_http_same_args_t *)same_args_node;
    while (!my_queue_empty(&same_args->args)) {
        q = my_queue_head(&same_args->args);
        tmp_arg = my_queue_data(q, my_http_arg_t, queue_in_same_args);
        my_queue_remove(&tmp_arg->queue_in_same_args);
        my_queue_remove(&tmp_arg->queue_in_args);
    }

    my_rbtree_delete(&args->same_args_tree, same_args_node);
}

// dump arg for test
void
my_http_arg_dump(my_http_arg_t *arg)
{
    char buf[1024];
    /* arg name: arg value\0 */
    if (arg->name.len + 2 + arg->value.len + 1 > (int64_t)sizeof(buf)) {
        MY_LOG_DEBUG("arg is to long");
        return;
    }

    int64_t offset = 0;
    strncpy(buf + offset, arg->name.data, (size_t)arg->name.len);
    offset += arg->name.len;
    buf[offset] = ':';
    offset++;
    buf[offset] = ' ';
    offset++;
    strncpy(buf + offset, arg->value.data, (size_t)arg->value.len);
    offset += arg->value.len;
    buf[offset] = '\0';

    MY_LOG_DEBUG("%s", buf);
}

// dump same args for test
void
my_http_same_args_dump(my_http_same_args_t *same_args)
{
    my_queue_t      *q = NULL;
    my_http_arg_t   *arg = NULL;

    q = my_queue_head(&same_args->args);
    while (q != &same_args->args) {
        arg = my_queue_data(q, my_http_arg_t, queue_in_same_args);
        my_http_arg_dump(arg);
        q = q->next;
    }
}

// dump args for test
void
my_http_args_dump(my_http_args_t *args)
{
    my_queue_t      *q = NULL;
    my_http_arg_t   *arg = NULL;

    q = my_queue_head(&args->args);
    while (q != &args->args) {
        arg = my_queue_data(q, my_http_arg_t, queue_in_args);
        my_http_arg_dump(arg);
        q = q->next;
    }
}

void
my_http_args_parser(my_http_args_t *args, my_str_t *args_str)
{
    my_str_t    name       = MY_NULL_STRING;
    my_str_t    value      = MY_NULL_STRING;
    my_bool_t   has_delimiter = MY_FALSE;

    char *p, *q, *end;
    my_bool_t parsing_value;
    my_http_arg_t *arg;

    p = args_str->data;
    q = p;
    end = p + args_str->len;
    parsing_value = MY_FALSE;

    while (p != end) {
        if (*p == '=' && !parsing_value) {
            has_delimiter = MY_TRUE;
            /* key data is between p and q */
            name.data = q;
            name.len = p - q;
            /* skip the current '=' char */
            p++;
            q = p;
            parsing_value = MY_TRUE;
        } else if (*p == '&') {
            if (parsing_value) {
                parsing_value = MY_FALSE;
                value.data = q;
                value.len = p - q;
            } else {
                name.data = q;
                name.len = p - q;
                value.data = NULL;
                value.len = 0;
            }

            /* skip the current '&' char */
            p++;
            q = p;

            if (name.len != 0) {
                arg = my_pool_mem_calloc(args->pool, sizeof(my_http_arg_t));
                arg->name = name;
                arg->value = value;
                arg->has_delimiter = has_delimiter ? 1 : 0;
                my_http_args_add(args, arg);
            }

            MY_STR_SET_NULL(&name);
            MY_STR_SET_NULL(&value);
            has_delimiter = MY_FALSE;
        } else {
            p++;
        }
    }

    if (p != q || parsing_value) {
        if (parsing_value) {
            value.data = q;
            value.len = p - q;
        } else {
            name.data = q;
            name.len = p - q;
            value.data = NULL;
            value.len = 0;
        }

        if (name.len != 0) {
            arg = my_pool_mem_calloc(args->pool, sizeof(my_http_arg_t));
            arg->name = name;
            arg->value = value;
            arg->has_delimiter = has_delimiter ? 1 : 0;
            my_http_args_add(args, arg);
        }
    }
}

void
my_http_args_to_string(my_http_args_t *args, my_str_t *dst_str)
{
    int64_t         len = 0;
    my_queue_t      *q = NULL;
    my_http_arg_t   *arg = NULL;

    q = my_queue_head(&args->args);
    while (q != &args->args) {
        arg = my_queue_data(q, my_http_arg_t, queue_in_args);
        // name=value&
        if (arg->value.len == 0 && !arg->has_delimiter) {
            len = len + arg->name.len + 1;
        } else {
            len = len + arg->name.len + 1 + arg->value.len + 1;
        }
        q = q->next;
    }

    if (len == 0) {
        dst_str->data = NULL;
        dst_str->len = 0;
        return;
    }

    char *str = my_pool_mem_calloc(args->pool, len);
    char *p = str;
    q = my_queue_head(&args->args);
    while (q != &args->args) {
        arg = my_queue_data(q, my_http_arg_t, queue_in_args);
        my_copy(p, arg->name.data, arg->name.len);
        p += arg->name.len;

        if (arg->value.len == 0 && !arg->has_delimiter) {
            // do nothing.
        } else {
            *p++ = '=';
            my_copy(p, arg->value.data, arg->value.len);
            p += arg->value.len;
        }

        *p++ = '&';
        q = q->next;
    }

    // replace last char '&' to '\0'
    str[len-1] = '\0';
    dst_str->data = str;
    dst_str->len = len - 1;
}